package domaine;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;

@Entity
@Table(name = "PARTIES", schema = "ZELDAVOLO")
public class Partie implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6401565573973653515L;

	public enum Etat {
		EN_ATTENTE {
			@Override
			boolean commencerPartie(Partie partie) {
				partie.etat = LANCEE;
				partie.debut = new GregorianCalendar();
				return true;
			}

			@Override
			boolean ajouterJoueur(Joueur joueur, Partie partie) {
				if (partie.gemmes.size() >= 6)
					return false;
				partie.gemmes.put(joueur, 5);
				return true;
			}

			@Override
			boolean desinscrire(Joueur joueur, Partie partie) {
				partie.gemmes.remove(joueur);
				return true;
			}
		},
		ANNULEE {

		},
		LANCEE {
			@Override
			boolean desinscrire(Joueur joueur, Partie partie) {
				partie.gemmes.remove(joueur);
				return true;
			}

			Tour jouerUnTour(Partie p) {
				Tour t = new Tour();
				t.jouerLeTour(p);
				p.listeTours.add(t);
				return t;
			}

			protected boolean terminerPartie(Partie partie) {
				partie.etat = Etat.TERMINEE;
				partie.fin = new GregorianCalendar();
				return true;
			}
		},
		TERMINEE {

		};

		boolean commencerPartie(Partie partie) {
			return false;
		}

		boolean ajouterJoueur(Joueur joueur, Partie partie) {
			return false;
		}

		boolean desinscrire(Joueur joueur, Partie partie) {
			return false;
		}

		Tour jouerUnTour(Partie p) {
			return null;
		}

		protected boolean terminerPartie(Partie partie) {
			return false;
		}
	}

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;

	@NotNull
	private String nom;

	@Temporal(TemporalType.TIMESTAMP)
	private Calendar debut;

	@Temporal(TemporalType.TIMESTAMP)
	private Calendar fin;

	@Enumerated(EnumType.STRING)
	private Etat etat;

	@ElementCollection
	@CollectionTable(name = "JOUEURS_GEMMES")
	@Column(name = "GEMMES")
	@MapKeyJoinColumn(name = "JOUEUR_ID")
	private Map<Joueur, Integer> gemmes;

	@OneToMany(cascade = { CascadeType.ALL })
	@JoinColumn(name = "PARTIE_ID")
	private List<Tour> listeTours;

	protected Partie() {

	}

	public Partie(Joueur joueur, String nom) {
		this.etat = Etat.EN_ATTENTE;
		this.nom = nom;
		gemmes = new HashMap<Joueur, Integer>();
		listeTours = new ArrayList<Tour>();
		gemmes.put(joueur, 5);
	}

	public boolean lancee() {
		return this.etat == Etat.LANCEE;
	}
	
	public boolean annulee() {
		return this.etat == Etat.ANNULEE;
	}
	
	public boolean terminee() {
		return this.etat == Etat.TERMINEE;
	}
	
	public boolean enAttente() {
		return this.etat == Etat.EN_ATTENTE;
	}

	public boolean commencerPartie() {
		return etat.commencerPartie(this);
	}

	public boolean ajouterJoueur(Joueur joueur) {
		return etat.ajouterJoueur(joueur, this);
	}

	public boolean desinscrire(Joueur joueur) {
		return etat.desinscrire(joueur, this);
	}

	public int points(Joueur joueur) {
		if (!gemmes.containsKey(joueur))
			return -1;
		return gemmes.get(joueur);
	}

	public Tour jouerUnTour(Partie p) {
		return etat.jouerUnTour(this);
	}

	protected void enleverUneVie(Joueur joueur) {
		int gemmesRestantes = gemmes.get(joueur) - 1;
		gemmes.put(joueur, gemmesRestantes);
	}

	protected void ajouterUneVie(Joueur joueur) {
		int gemmesRestantes = gemmes.get(joueur);
		if (gemmesRestantes >= 5)
			return;
		gemmesRestantes++;
		gemmes.put(joueur, gemmesRestantes);
	}

	public boolean terminerPartie() {
		return etat.terminerPartie(this);
	}

	public Iterator<Joueur> joueurs() {
		return gemmes.keySet().iterator();
	}

	public int getId() {
		return id;
	}

	public String getNom() {
		return nom;
	}

	public void setNom(String nom) {
		this.nom = nom;
	}

	public Calendar getDebut() {
		return debut;
	}

	public void setDebut(Calendar debut) {
		this.debut = debut;
	}

	public Calendar getFin() {
		return fin;
	}

	public void setFin(Calendar fin) {
		this.fin = fin;
	}

	public Etat getEtat() {
		return etat;
	}

	public boolean setEtat(Etat etat) {
		if (this.etat.equals(Etat.ANNULEE) || this.etat.equals(Etat.TERMINEE))
			return false;
		if (this.etat.equals(etat.LANCEE)
				&& (etat.equals(Etat.EN_ATTENTE) || etat.equals(Etat.ANNULEE)))
			return false;
		if (etat.equals(Etat.ANNULEE) && !this.etat.equals(Etat.EN_ATTENTE))
			return false;
		this.etat = etat;
		return true;
	}

	public int getNbJoueurs() {
		return gemmes.size();
	}
	
	public int getNbTours() {
		return listeTours.size();
	}
	
	public List<Tour> getTours(){
		return listeTours;
	}

	public Collection<Joueur> getJoueurs() {
		return gemmes.keySet();
	}
	
	public Map<Joueur, Integer> getGemmes() {
		return gemmes;
	}
	
	public int getGemmesDunJoueur(Joueur j){
		return gemmes.get(j);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		result = prime * result + ((nom == null) ? 0 : nom.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Partie other = (Partie) obj;
		if (id != other.id)
			return false;
		if (nom == null) {
			if (other.nom != null)
				return false;
		} else if (!nom.equals(other.nom))
			return false;
		return true;
	}
}
